package net.courier.remoting.provider.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import net.courier.remoting.server.ServiceServer;

public class NettyServiceServer extends ServiceServer {
	private static final int DEFAULT_MAX_OBJECT_SIZE = 1048576;
	 

	private int maxObjectSize = DEFAULT_MAX_OBJECT_SIZE;
	
	private Integer connectTimeoutMillis;
	private Boolean keepAlive = true;
	private Integer sendBufferSize;
	private Integer receiveBufferSize;
	private Integer socketTimeout;
	private Boolean autoRead;
	private Boolean tcpNodelay;

	public void setMaxObjectSize(int maxObjectSize) {
		this.maxObjectSize = maxObjectSize;
	}
	
	public void setConnectTimeoutMillis(int connectTimeoutMillis) {
		this.connectTimeoutMillis = connectTimeoutMillis;
	}
	
	public void setKeepAlive(boolean keepAlive) {
		this.keepAlive = keepAlive;
	}
	
	public void setSendBufferSize(int sendBufferSize) {
		this.sendBufferSize = sendBufferSize;
	}
	
	public void setReceiveBufferSize(int receiveBufferSize) {
		this.receiveBufferSize = receiveBufferSize;
	}
	
	public void setAutoRead(Boolean autoRead) {
		this.autoRead = autoRead;
	}
	
	public void setSocketTimeout(Integer socketTimeout) {
		this.socketTimeout = socketTimeout;
	}
	
	public void setTcpNodelay(Boolean tcpNodelay) {
		this.tcpNodelay = tcpNodelay;
	}

	@Override
	public void start() throws Exception {
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		try {
			ServerBootstrap bootstrap = new ServerBootstrap();
			bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class);
			setOptions(bootstrap).childHandler(new ChannelInitializer<SocketChannel>() {
				@Override
				public void initChannel(SocketChannel ch) throws Exception {
					ch.pipeline().addLast(new ObjectEncoder(),
							new ObjectDecoder(maxObjectSize, ClassResolvers.weakCachingResolver(null)),
							new MethodInvokeHandler(registry));
				}
			});

			bootstrap.bind(port).sync().channel().closeFuture().sync();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}

	}

	private ServerBootstrap setOptions(ServerBootstrap bootstrap) {
	    bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMillis);
	    bootstrap.option(ChannelOption.AUTO_READ, autoRead);
		bootstrap.option(ChannelOption.SO_KEEPALIVE, keepAlive);
		bootstrap.option(ChannelOption.SO_SNDBUF, sendBufferSize);
		bootstrap.option(ChannelOption.SO_RCVBUF, receiveBufferSize);
		bootstrap.option(ChannelOption.SO_TIMEOUT, socketTimeout);
		bootstrap.option(ChannelOption.TCP_NODELAY, tcpNodelay);
		return bootstrap;
	}

	public static void main(String[] args) throws Exception {
		(new NettyServiceServer()).start();
	}

}
